
#pragma once

#include "..\\ExternalTools\\ExternalTools.h"

#include "..\\Loirey\\loirey_GLOBAL.h"
#include "..\\Loirey\\loirey_MyMath.h"
#include "..\\Loirey\\loirey_BasicStructure.h"

#include "cvf_ImageTransform.h"

using namespace loirey;

class CImageFilter
{
protected:
	int _SelfIndex;
	int _OutputOffset;

	int _InputDimension;
	int _OutputDimension;

	int _ResponseUserAmount;

	int _Amount_ReportGot;
	//int _ReportedChildrenAmount;
public:
	bool fIsCalculationDone;
public:
	int AreaThresholdForResponseCacheUsage;
	CSimpleTypeArray<int> InputFilterIndexList;
	CSimpleTypeArray<int> InputResponseIndexList;
	CSimpleTypeArray<int> OutputIndexList;
	CSimpleTypeArray<double> ResponseList;

public:
	CImageFilter();
	virtual ~CImageFilter();

	virtual void Clear_IO_Relationship();
	virtual void Copy_IO_Settings(const CImageFilter* pImageFilterPrototype);

	virtual void SetSelfIndex(int SelfIndex);
	virtual int GetSelfIndex();
	virtual void SetOutputOffset(int OutputOffset);

	virtual int GetInputDimension();
	virtual int GetOutputDimension();

	virtual void PushBackInputResponse(CImageFilter* pFilter, int ResponseIndex);
	virtual void PushBackOutput(int ResponseIndex);

	virtual size_t GetMemoryUsage() const;
public:
	//virtual bool IsCalculationDone();
	virtual void ClearContent();
	virtual void MakeFN_OutputCache(char* DstChar, const char* ccpPN_Cache, const char* ccpFN_ImageHash);
	virtual void Work(
		int W, int H, CImageFilter** ppInputFilterListBase, double* pOutput,
		const char* ccpPN_Cache, const char* ccpFN_ImageHash);
protected:
	virtual void _ReportedByChildren();

public:
	virtual CImageFilter* myClone() const = 0;
	virtual int GetResponseDimension() = 0;
protected:
	virtual void _ComputeResponse(int W, int H, double** ppInputResponse, double* pResponse) = 0;
};

class CImageFilter_RGB : public CImageFilter
{
public:
	template<typename T>
	void ComputeRGB(const CImg<T>& SourceImageRGB, double* pOutput)
	{
		int i,j,iv;
		int WH = SourceImageRGB.dimx() * SourceImageRGB.dimy();
		const T* qd;

		_Amount_ReportGot = 0;
		//_ReportedChildrenAmount = -1;

		ResponseList.Resize(WH * 3);
		double* _pd = ResponseList.pElement;
		for ( i = 0; i < 3; ++i )
		{
			iv = (i<SourceImageRGB.dimv() ? i : SourceImageRGB.dimv()-1);
			qd = SourceImageRGB.data + WH * (0 + SourceImageRGB.dimz() * iv);
			for ( j = 0; j < WH; ++j )
			{
				*(_pd++) = (double)(*(qd++));
			}
		}

		int o_dim = GetOutputDimension();
		if (pOutput != NULL)
		{
			const double* qd;
			double* pd = pOutput + ((size_t)WH) * (size_t)_OutputOffset;
			for ( i = 0; i < o_dim; ++i )
			{
				qd = ResponseList.pElement + WH * OutputIndexList.pElement[i];
				for ( j = 0; j < WH; ++j )
					*(pd++) = *(qd++);
			}
		}

		fIsCalculationDone = true;
		//_ReportedByChildren();
	}
public:
	CImageFilter* myClone() const;
	int GetResponseDimension();
protected:
	virtual void _ComputeResponse(int W, int H, double** ppInputResponse, double* pResponse);
};

class CImageFilterBank
{
protected:
	int _OutputDimension;
	string strPN_Cache;
public:
	int ImageHandle;
	int W, H, WH;
	string strFN_ImageCacheHashName;
public:
	CSimpleTypeArray<CImageFilter*> OrderedFilterBank;
	CSimpleTypeArray<double> OutputList;
	CSimpleTypeArray<bool> FlagGotOutputValueList;
	CSimpleTypeArray<int> FilterOutputStartOffsetList;
	//CSimpleTypeArray<bool> FilterComputedFlagList;
	CImageFilter_RGB* pBasicFilter_RGB;

protected:
	void myRelease();
public:
	CImageFilterBank();
	CImageFilterBank(const CImageFilterBank& AnotherInstance);
	CImageFilterBank& operator = (const CImageFilterBank& AnotherInstance);
	CImageFilterBank* myClone() const;
	virtual ~CImageFilterBank();
public:
	virtual void SetCachePath(string strPN_Cache);
public:
	virtual void myInit();
	virtual size_t GetMemoryUsage() const;
public:
	virtual int GetOutputDimension(bool fShouldRecompute = false);
	virtual void PushBackFilter(CImageFilter* pImageFilter);
public:
	virtual const double* GetOutput(int OutputIndex);
public:
	virtual void ClearContent();
public:
	template<typename T>
	int Work(const CImg<T>& SourceImageRGB, string strFN_ImageCacheHashName = "")
	{
		this->ClearContent();

		ImageHandle = CMyRandom::NextInt(2147483647)+1;

		W = SourceImageRGB.dimx();
		H = SourceImageRGB.dimy();
		WH = W * H;
		this->strFN_ImageCacheHashName = strFN_ImageCacheHashName;

		size_t o_dim = GetOutputDimension();

		OutputList.Resize(WH * o_dim);
		pBasicFilter_RGB->ComputeRGB(SourceImageRGB, OutputList.pElement);
		//FilterComputedFlagList.pElement[0] = true;

		////////////CImageFilter** pImageFilterBase;
		////////////int i;
		////////////OutputList.DeepClear();
		////////////OutputList.Resize(WH * o_dim);

		////////////FilterComputedFlagList.Resize(OrderedFilterBank.Size);
		////////////FilterComputedFlagList.Fill(false);

		////////////FlagGotOutputValueList.Resize(o_dim);
		////////////FlagGotOutputValueList.Fill(false);

		//////////////CExternalTools::le_myTimer_B.Stop();
		//////////////CExternalTools::le_myTimer_A.Start();

		////////////pImageFilterBase = OrderedFilterBank.pElement;
		////////////for ( i = 0; i < (int)OrderedFilterBank.Size; ++i )
		////////////{
		////////////	//CExternalTools::le_myTimer_D.Start();

		////////////	if (i == 0)
		////////////	{
		////////////		pBasicFilter_RGB->ComputeRGB(SourceImageRGB, OutputList.pElement);
		////////////		FilterComputedFlagList.pElement[i] = true;
		////////////	}
		////////////	else
		////////////		pImageFilterBase[i]->ClearContent();
		////////////	
		////////////	//CExternalTools::le_myTimer_D.Stop();
		////////////	//cout<<i<<" "<<CExternalTools::le_myTimer_D.GetElapsedTime()<<endl;
		////////////}

		//////////////CExternalTools::le_myTimer_A.Stop();
		//////////////CExternalTools::le_myTimer_B.Start();

		return ImageHandle;
	}
};

class CIntegralResponse
{
public:
	int W, H, WH, xBoundary, yBoundary;
	CSimpleTypeArray<double> Data;

public:
	static void TranlateIntRectBoxToXYLowHigh(int& Dst_xL, int& Dst_yL, int& Dst_xH, int& Dst_yH, const CRectBox<int>& SrcBox)
	{
		Dst_xL = SrcBox.x - 1;
		Dst_yL = SrcBox.y - 1;
		Dst_xH = SrcBox.x - 1 + SrcBox.w;
		Dst_yH = SrcBox.y - 1 + SrcBox.h;
	}
public:
	CIntegralResponse()
	{
		ClearContent();
	}

	void ClearContent()
	{
		W = H = WH = 0;
		xBoundary = yBoundary = 0;
		Data.DeepClear();
	}

	size_t GetMemoryUsage() const
	{
		return sizeof(int) * 3 + Data.GetMemoryUsage();
	}

	void LoadFromFile(string strFN_IntRes)
	{
		FILE* FIN = fopen(strFN_IntRes.c_str(), "rb");
		fread(&W, sizeof(W), 1, FIN);
		fread(&H, sizeof(H), 1, FIN);
		fread(&xBoundary, sizeof(xBoundary), 1, FIN);
		fread(&yBoundary, sizeof(yBoundary), 1, FIN);
		WH = W * H;
		Data.Resize(WH);
		fread(Data.pElement, sizeof(*Data.pElement), WH, FIN);
		fclose(FIN);
	}

	void SaveToFile(string strFN_IntRes)
	{
		FILE* FOUT = fopen(strFN_IntRes.c_str(), "wb");
		fwrite(&W, sizeof(W), 1, FOUT);
		fwrite(&H, sizeof(H), 1, FOUT);
		fwrite(&xBoundary, sizeof(xBoundary), 1, FOUT);
		fwrite(&yBoundary, sizeof(yBoundary), 1, FOUT);
		fwrite(Data.pElement, sizeof(*Data.pElement), WH, FOUT);
		fclose(FOUT);
	}

	void myInit(const double* SrcResponse, int W, int H, int Boundary = 0)
	{
		myInit(SrcResponse, W, H, Boundary, Boundary);
	}

	void myInit(const double* SrcResponse, int W, int H, int xBoundary, int yBoundary)
	{
		int ix,iy;
		double* pd;
		const double* qd;

		this->W = W; this->H = H; this->WH = W * H;
		this->xBoundary = xBoundary;
		this->yBoundary = yBoundary;
		Data.Resize(WH);
		for ( iy = 0; iy < H; ++iy )
		{
			pd = Data.pElement + iy * W;
			qd = SrcResponse + iy * W;
			for ( ix = 0; ix < W; ++ix, ++pd )
				*pd = *(qd++) + (ix>0 ? *(pd-1) : 0.0);

			if (iy == 0) continue;

			pd = Data.pElement + iy * W;
			qd = pd - W;
			for ( ix = 0; ix < W; ++ix )
				*(pd++) += *(qd++);
		}
	}

	double CalcRectangle(int xL, int yL, int xH, int yH, bool fDoAverage, bool fFixArea = true) const
	{
		if (xL>=xH || yL>=yH || W<1 || H<1) return 0.0;

		if (xL >= W - 1 - xBoundary)
		{
			xH += W - 2 - xL - xBoundary;
			xL = W - 2 - xBoundary;
		}
		else if (xH < xBoundary)
		{
			xL += xBoundary - xH;
			xH = xBoundary;
		}
		if (yL >= H - 1 - yBoundary)
		{
			yH += H - 2 - yL - yBoundary;
			yL = H - 2 - yBoundary;
		}
		else if (yH < yBoundary)
		{
			yL += yBoundary - yH;
			yH = yBoundary;
		}

		double fake_area = (xL-xH) * (yL-yH);
		xL = (xL<0 ? -1 : xL); xH = (xH>=W ? W-1 : xH);
		yL = (yL<0 ? -1 : yL); yH = (yH>=H ? H-1 : yH);
		int fx = xH - xL;
		int fy = yH - yL;
		double fake_real_ratio = fake_area / (fx * fy);

		fy *= W;
		const double* qd = Data.pElement + xH + yH * W;
		double ret = *qd;
		if (xL>=0) ret -= *(qd-fx);
		if (yL>=0) ret -= *(qd-fy);
		if (xL>=0 && yL>=0) ret += *(qd-fx-fy);

		if (fFixArea) ret *= fake_real_ratio;
		if (fDoAverage) ret /= fake_area;

		return ret;
	}

	inline double CalcRectangle(const CRectBox<int>& Box, bool fDoAverage, bool fFixArea = false) const
	{
		int xl,yl,xh,yh;
		TranlateIntRectBoxToXYLowHigh(xl,yl,xh,yh,Box);
		return CalcRectangle(xl, yl, xh, yh, fDoAverage, fFixArea);
	}

	inline double CalcSinglePixel(int x, int y) const
	{
		return CalcRectangle(x-1, y-1, x, y, false, false);
	}
};

class CIntegralMeanAndVariance
{
public:
	CIntegralResponse mapX;
	CIntegralResponse mapXX;

public:
	CIntegralMeanAndVariance()
	{
		ClearContent();
	}

	void ClearContent()
	{
		mapX.ClearContent();
		mapXX.ClearContent();
	}

	void myInit(const double* SrcResponse, int W, int H, int Boundary = 0)
	{
		myInit(SrcResponse, W, H, Boundary, Boundary);
	}

	void myInit(const double* SrcResponse, int W, int H, int xBoundary, int yBoundary)
	{
		int i;
		int WH = W * H;

		mapX.myInit(SrcResponse, W, H, xBoundary, yBoundary);

		CSimpleTypeArray<double> temp_xx;
		temp_xx.Resize(WH);
		double* pd = temp_xx.pElement;
		const double* qd = SrcResponse;
		for ( i = 0; i < WH; ++i ) *(pd++) = CMyMath::sqr(*(qd++));

		mapXX.myInit(temp_xx.pElement, W, H, xBoundary, yBoundary);
	}

	size_t GetMemoryUsage() const
	{
		return mapX.GetMemoryUsage() + mapXX.GetMemoryUsage();
	}

	void LoadFromFile(string strFN_IntRes)
	{
		FILE* FIN = fopen(strFN_IntRes.c_str(), "rb");

		fread(&mapX.W, sizeof(mapX.W), 1, FIN);
		fread(&mapX.H, sizeof(mapX.H), 1, FIN);
		fread(&mapX.xBoundary, sizeof(mapX.xBoundary), 1, FIN);
		fread(&mapX.yBoundary, sizeof(mapX.yBoundary), 1, FIN);
		mapX.WH = mapX.W * mapX.H;
		mapXX.W = mapX.W;
		mapXX.H = mapX.H;
		mapXX.WH = mapX.WH;
		mapXX.xBoundary = mapX.xBoundary;
		mapXX.yBoundary = mapX.yBoundary;

		mapX.Data.Resize(mapX.WH);
		fread(mapX.Data.pElement, sizeof(*mapX.Data.pElement), mapX.WH, FIN);
		mapXX.Data.Resize(mapXX.WH);
		fread(mapXX.Data.pElement, sizeof(*mapXX.Data.pElement), mapXX.WH, FIN);

		fclose(FIN);
	}

	void SaveToFile(string strFN_IntRes)
	{
		FILE* FOUT = fopen(strFN_IntRes.c_str(), "wb");
		fwrite(&mapX.W, sizeof(mapX.W), 1, FOUT);
		fwrite(&mapX.H, sizeof(mapX.H), 1, FOUT);
		fwrite(&mapX.xBoundary, sizeof(mapX.xBoundary), 1, FOUT);
		fwrite(&mapX.yBoundary, sizeof(mapX.yBoundary), 1, FOUT);
		fwrite(mapX.Data.pElement, sizeof(*mapX.Data.pElement), mapX.WH, FOUT);
		fwrite(mapXX.Data.pElement, sizeof(*mapXX.Data.pElement), mapXX.WH, FOUT);
		fclose(FOUT);
	}

	void CalcRectangle(double& DstMean, double& DstVariance, int xL, int yL, int xH, int yH) const
	{
		DstVariance = mapXX.CalcRectangle(xL, yL, xH, yH, true, false);
		DstMean = mapX.CalcRectangle(xL, yL, xH, yH, true, false);
		double ret2 = DstVariance - DstMean * DstMean;
		DstVariance = (ret2>0 ? sqrt(ret2) : 0.0);
	}

	inline void CalcRectangle(double& DstMean, double& DstVariance, const CRectBox<int>& Box) const
	{
		int xl,yl,xh,yh;
		CIntegralResponse::TranlateIntRectBoxToXYLowHigh(xl,yl,xh,yh,Box);
		CalcRectangle(DstMean, DstVariance, xl, yl, xh, yh);
	}
};

